/*
 * This file is part of the Shell Library.
 *
 * Copyright (c) 2024, ark, <345473553@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Function: Portable interface functions.
 */

#include "shell.h"
#include <stdlib.h>
#include <string.h>
#include "elog.h"
#include "re.h"
// #include "com_func.h"

/*具体解析的实现可参考D:\MyHc32Prj_L13x\projects\wireless_transer\USER\shell_port.c中的例子。*/

uint8_t _u8sShellCache[128];

/*********************************************************************
 * EXTERN FUNCTIONS
 *********************************************************************/
#if(SHELL_NEED_LOGIN==1)
uint8_t shell_parse_login(uint8_t argc, const char *argv[]);
#endif
uint8_t shell_parse_help(uint8_t argc, const char *argv[]);
uint8_t shell_parse_int(uint8_t argc, const char *argv[]);
uint8_t shell_parse_uint(uint8_t argc, const char *argv[]);
uint8_t shell_parse_hex(uint8_t argc, const char *argv[]);
#if(SHELL_NEED_FLOAT==1)
uint8_t shell_parse_flt(uint8_t argc, const char *argv[]);
#endif
uint8_t shell_parse_str(uint8_t argc, const char *argv[]);
uint8_t shell_parse_md(uint8_t argc, const char *argv[]);
uint8_t shell_parse_ram(uint8_t argc, const char *argv[]);
uint8_t shell_parse_callv(uint8_t argc, const char *argv[]);
uint8_t shell_parse_callh(uint8_t argc, const char *argv[]);
uint8_t shell_parse_calld(uint8_t argc, const char *argv[]);

/*********************************************************************
 * FUNCTIONS
 *********************************************************************/
uint8_t shell_parse_reset(uint8_t argc, const char *argv[])
{
	// __NVIC_SystemReset();
  return SHELL_OK;
}

/*
 * cmd: PA  <dec:pin> [0/1]
 */
uint8_t shell_parse_pa(uint8_t argc, const char *argv[])
{
	uint8_t reg,val;
	uint32_t pin;
	
	if( argc == 2 ){
		if( SHELL_OK != verify_dec_u8(argv[1]))
			return SHELL_ERR_PARAM;
		reg = 0; // pb pin
	}else if( argc == 3 ){
		if( SHELL_OK != verify_dec_u8(argv[1]))
			return SHELL_ERR_PARAM;
		if( SHELL_OK != verify_dec_u8(argv[2]))
			return SHELL_ERR_PARAM;
		reg = 1; // pb pin val
	}else{
		return SHELL_ERR_PARAM;
	}
	
	pin = atoi((const char*)argv[1]);
	if( pin > 15 )
		return SHELL_ERR_PARAM;

	if( reg == 0 ){
		// val = (uint8_t)Gpio_GetInputIO(GpioPortA, (en_gpio_pin_t)pin);
		elog_raw("GPIO PA%d read %d ",pin, val);
	}else{
		val = (uint8_t)atoi((const char*)argv[2]);
    if( val == 0 )
      // Gpio_WriteOutputIO(GpioPortA, (en_gpio_pin_t)pin, FALSE);
      (void*)0;
		else if( val == 1 )
      // Gpio_WriteOutputIO(GpioPortA, (en_gpio_pin_t)pin, TRUE);
      (void*)0;
    else
			return SHELL_ERR_PARAM;
		elog_raw("GPIO PA%d write %d ",pin, val);
	}
	
  return SHELL_OK;
}


/*
 * cmd: PB  <dec:pin> [0/1]
 */
uint8_t shell_parse_pb(uint8_t argc, const char *argv[])
{
	uint8_t reg,val;
	uint32_t pin;
	
	if( argc == 2 ){
		if( SHELL_OK != verify_dec_u8(argv[1]))
			return SHELL_ERR_PARAM;
		reg = 0; // pb pin
	}else if( argc == 3 ){
		if( SHELL_OK != verify_dec_u8(argv[1]))
			return SHELL_ERR_PARAM;
		if( SHELL_OK != verify_dec_u8(argv[2]))
			return SHELL_ERR_PARAM;
		reg = 1; // pb pin val
	}else{
		return SHELL_ERR_PARAM;
	}
	
	pin = atoi((const char*)argv[1]);
	if( pin > 15 )
		return SHELL_ERR_PARAM;

	if( reg == 0 ){
		// val = (uint8_t)Gpio_GetInputIO(GpioPortB, (en_gpio_pin_t)pin);
		elog_raw("GPIO PB%d read %d ",pin, val);
	}else{
		val = (uint8_t)atoi((const char*)argv[2]);
    if( val == 0 )
      // Gpio_WriteOutputIO(GpioPortB, (en_gpio_pin_t)pin, FALSE);
      (void*)0;
		else if( val == 1 )
      // Gpio_WriteOutputIO(GpioPortB, (en_gpio_pin_t)pin, TRUE);
      (void*)0;
    else
			return SHELL_ERR_PARAM;
		elog_raw("GPIO PB%d write %d ",pin, val);
	}
	
  return SHELL_OK;
}

/*
 * cmd: RTC  [<xxxx-xx-xx> <xx:xx:xx> <x>]
 */
char* u8_find_next( char *psrc, uint8_t val )
{
  while(*psrc!=val){
    psrc++;
  };
  return psrc;
}
uint8_t u8sReDate[]="^\\d+-\\d+-\\d+$";
uint8_t u8sReTime[]="^\\d+:\\d+:\\d+$";
uint8_t shell_parse_rtc(uint8_t argc, const char *argv[])
{
  char *psc,*psn;
	uint8_t reg,len;
  uint8_t month,date,hour,min,sec,week;
  uint16_t year;
  // stc_rtc_time_t time;
	
	if( argc == 1 ){
		reg = 0; // pb pin
	}else if( argc == 4 ){
		if( -1 == re_match((const char*)u8sReDate,(const char*)argv[1],(int*)&len))
			return SHELL_ERR_PARAM;
		if( -1 == re_match((const char*)u8sReTime,(const char*)argv[2],(int*)&len))
			return SHELL_ERR_PARAM;
		if( SHELL_OK != verify_dec_u8(argv[3]))
			return SHELL_ERR_PARAM;
		reg = 3; // date time week
	}else{
		return SHELL_ERR_PARAM;
	}
  if(reg==3){
    /* year month date */
    psc = (char*)argv[1];
    psn = u8_find_next(psc,'-');
    *psn = 0x00;
  	year = atoi((const char*)psc);
    
    psc = psn+1;
    psn = u8_find_next(psc,'-');
    *psn = 0x00;
  	month = atoi((const char*)psc);
    
    psc = psn+1;
  	date = atoi((const char*)psc);
    
    /* hour min sec */
    psc = (char*)argv[2];
    psn = u8_find_next(psc,':');
    *psn = 0x00;
  	hour = atoi((const char*)psc);
    
    psc = psn+1;
    psn = u8_find_next(psc,':');
    *psn = 0x00;
  	min = atoi((const char*)psc);
    
    psc = psn+1;
  	sec = atoi((const char*)psc);
    
    /* week */
  	week = atoi((const char*)argv[3]);
  	if( week > 7 )
  		return SHELL_ERR_PARAM;
  }

	// if( reg == 0 ){
  //   Rtc_ReadDateTime(&time);
  //   elog_raw("RTC get %d-%2d-%2d %2d:%2d:%2d %d/7\r\n",time.u8Year,time.u8Month,time.u8Day,time.u8Hour,time.u8Minute,time.u8Second,time.u8DayOfWeek);
	// }else{
  //   time.u8Year=year;time.u8Month=month;time.u8Day=date;
  //   time.u8Hour=hour;time.u8Minute=min;time.u8Second=sec;time.u8DayOfWeek=week;
  //   Rtc_SetTime(&time);
	// 	elog_raw("RTC set %d-%2d-%2d %2d:%2d:%2d %d.",year,month,date,hour,min,sec,week);
	// }
	
  return SHELL_OK;
}

void convert_str_to_ip( const char *pstr, uint8_t *pdst )
{
  uint8_t val;
  int sk=0,n=0;
  memset(pdst,0,4);
  while(pstr[sk]!='\0'){
    val = 0;
    while((pstr[sk]!='.')&&(pstr[sk]!='\0')){
      val = 10 * val + (pstr[sk] - '0');
      sk++;
    }
    pdst[n]=val;
    if(n<4) n++;
    if(pstr[sk]!='\0') sk++;
  }
  pdst[n]=val;
}

/*
 * cmd: IP <xxx.xxx.xxx.xx mask xxx.xxx.xxx.xxx>
 * desp: get or set the ip mask gateway*/
uint8_t shell_parse_ip(uint8_t argc, const char *argv[])
{
  uint8_t ip[4],gateway[4];
  uint16_t mask;
  int seek, len;
  const char re_ip[]="\\d+\\.\\d+\\.\\d+\\.\\d+";

  if( argc == 1 ){
		// 显示当前IP、掩码、网关
	}else if(argc!=4){
		return SHELL_ERR_PARAM;
	}else{
    // ip
    seek = re_match(re_ip, argv[1], &len);
    if(len==0)
      return SHELL_ERR_PARAM;
    // mask
    seek = re_match("\\d+", argv[2], &len);
    if(len==0)
      return SHELL_ERR_PARAM;
    // gateway
    seek = re_match(re_ip, argv[3], &len);
    if(len==0)
      return SHELL_ERR_PARAM;
    
    convert_str_to_ip(argv[1], ip);
    mask = (uint16_t)atoi((const char*)argv[2]);
    convert_str_to_ip(argv[3], gateway);
  }

  return SHELL_OK;
}

const char _u8sshellDespsLogin[] = "LOGIN [key:str]\r\n";
const char _u8sshellDespsHelp[] = "HELP\r\n";
const char _u8sshellDespsInt[] = "INT <adr:0xXX[XX]> <len:1|2|4> [dat:dec]\r\n";
const char _u8sshellDespsUint[] = "UINT <adr:0xXX[XX]> <len:1|2|4> [dat:dec]\r\n";
const char _u8sshellDespsHex[] = "HEX <adr:0xXX[XX]> <len:dec> [dat:0xXX[XX]]\r\n";
const char _u8sshellDespsFlt[] = "FLT <adr:0xXX[XX]> <len:4> [dat:flt]\r\n";
const char _u8sshellDespsStr[] = "STR <adr:0xXX[XX]> <len:1-96> [dat:str]\r\n";
const char _u8sshellDespsMd[] = "MD <adr|typ|len:0xXXXXXXXXf4> ...\r\n";
const char _u8sshellDespsMm[] = "MM  <adr:0xXXXXXXXX> [data:0xXX[XXXXXX]]\r\n";
const char _u8sshellDespsCallv[] = "CALLV <adr:0xXXXXXXXX> [params...]\r\n";
const char _u8sshellDespsCalld[] = "CALLD <adr:0xXXXXXXXX> [params...]\r\n";
const char _u8sshellDespsCallh[] = "CALLH <adr:0xXXXXXXXX> [params...]\r\n";
const char _u8sshellDesps[][48]={
  "RESET\r\n",
  "PA  <pin:dec> [0/1]\r\n",
  "PB  <pin:dec> [0/1]\r\n",
  "RTC [xx-xx-xx xx:xx:xx]\r\n",
  "IP [xx.xx.xx.xx xx xx.xx.xx.xx]\r\n",
};

const xShellCmd _xshellCmds[]={
#if(SHELL_NEED_LOGIN==1)
  {shell_parse_login,"LOGIN",_u8sshellDespsLogin},
#endif
  {shell_parse_help,"HELP",_u8sshellDespsHelp},
  {shell_parse_int,"INT",_u8sshellDespsInt},
  {shell_parse_uint,"UINT",_u8sshellDespsUint},
  {shell_parse_hex,"HEX",_u8sshellDespsHex},
#if(SHELL_NEED_FLOAT==1)
  {shell_parse_flt,"FLT",_u8sshellDespsFlt},
#endif
  {shell_parse_str,"STR",_u8sshellDespsStr},
  {shell_parse_md,"MD",_u8sshellDespsMd},
  {shell_parse_ram,"MM",_u8sshellDespsMm},
  {shell_parse_callv,"CALLV",_u8sshellDespsCallv},
  {shell_parse_calld,"CALLD",_u8sshellDespsCalld},
  {shell_parse_callh,"CALLH",_u8sshellDespsCallh},
  {shell_parse_reset,"RESET",_u8sshellDesps[0]},
  {shell_parse_pa,"PA",_u8sshellDesps[1]},
  {shell_parse_pb,"PB",_u8sshellDesps[2]},
  {shell_parse_rtc,"RTC",_u8sshellDesps[3]},
  {shell_parse_ip,"IP",_u8sshellDesps[4]},
  {NULL,"",(const char*)NULL},
};
